package cate.game.fight.test;

import cate.common.table.EExcelNoteField;
import cate.common.table.d.GDFight;
import cate.common.table.d.GDFunc;
import cate.common.table.d.GDPosition;
import cate.common.util.TimeTool;
import cate.common.util.XT;
import cate.game.GameBody;
import cate.game.client.msg.MergeMsgSender;
import cate.game.fight.msg.FightPlayResp;
import cate.game.framework.ThreadShare;
import cate.game.play.part.FightSide;
import cate.game.play.proce.play.PlayPO;
import cate.game.role.Role;
import cate.game.role.bag.hero.Hero;
import cate.game.role.fight.FuncPosition;
import cate.game.role.fight.HeroGrid;
import cate.game.role.fight.Position;
import easy.java.practice.errorcode.EcResult;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.xssf.usermodel.*;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

public class FightTestUtil {

	private static int[] TEST_HERO_STAR = {11, 13, 15};

	public static void run(Role role, int fightNum, int carryNum, int starDiff) {
		ThreadShare.instance().exe(carryNum - 1, () -> {
			System.out.println(carryNum + "核测试begin");
			XSSFWorkbook workbook = new XSSFWorkbook();
			for (int carryStar : TEST_HERO_STAR) {
				FightTestUtil.buildHero(role, carryStar, carryStar - starDiff);
				final String sheetName = carryStar + "星";
				final String taskName = carryNum + "核" + carryStar + "星英雄测试";
				System.out.println(taskName + "begin");
				FightTestUtil.buildSheet(workbook, sheetName, FightTestUtil.carryFight(taskName, role, carryStar, carryNum, fightNum).buildResult());
				System.out.println(taskName + "end");
			}
			FightTestUtil.out(workbook, carryNum + "核测试" + fightNum + "场");
			System.out.println(carryNum + "核测试end");
		});
	}


	private static void buildHero(Role role, int carryStar, int supportStar) {
		role.getBag().hero.getList().clear();
		role.getBag().spice.getList().clear();
		role.getGame().table.hero.base.list.forEach(
				tpl -> {
					if (tpl.real && tpl.gmProducible) {
						if (tpl.star != carryStar && tpl.star != supportStar) {
							return;
						}
						Hero hero = new Hero(role, tpl.id);
						HeroDevUtil.handleTopLevel(role, tpl, hero);
						if (carryStar >= 9) {
							HeroDevUtil.handleTopEquip(role, hero);
							HeroDevUtil.handleTopRune(role, tpl, hero);
						}
						if (carryStar >= 11) {
							HeroDevUtil.handleTopFairy(role, tpl, hero);
						}
						if (carryStar >= 12) {
							HeroDevUtil.handleTopAwakenFairy(role, tpl, hero);
						}
						if (carryStar >= 13) {
							HeroDevUtil.handleTopSpice(role, tpl, hero, new MergeMsgSender());
							HeroDevUtil.handleTopTalent(tpl, hero);
						}
						if (carryStar >= 15) {
							HeroDevUtil.handleFavor(role, tpl, hero);
							HeroDevUtil.handleYoke(role, tpl, hero, new MergeMsgSender());
						}
						role.getBag().hero.getList().add(hero);
					}
				});
	}


	public static FightTestStat jobFight(Role role, int fightNum) {
		FightSide a;
		FightSide b;
		FightTestStat stat = new FightTestStat();
		long calTime = 0L;
		List<Hero> allHero = new ArrayList<>(role.getBag().hero.getList());
		allHero.removeIf(h -> h.lock.heroYoke || h.lock.heroContract);
		List<Hero> dpsHero = new ArrayList<>();
		List<Hero> supportHero = new ArrayList<>();
		for (Hero hero : allHero) {
			if (hero.getTpl(role.getGame()).job <= 2) {
				dpsHero.add(hero);
			} else {
				supportHero.add(hero);
			}
		}
		for (int i = 0; i < fightNum; i++) {
			List<Hero> listA = randomHero(role.getGame(),dpsHero, supportHero,2 + (int) (Math.random() * 3));
			List<Hero> listB = randomHero(role.getGame(),dpsHero, supportHero,2 + (int) (Math.random() * 3));
			a = FightTestUtil.buildRandomFightSide(role, listA, GDFight.Team.A);
			b = FightTestUtil.buildRandomFightSide(role, listB, GDFight.Team.B);
			long start = System.currentTimeMillis();
			EcResult<PlayPO> r = role.getGame().play.startBySystem(new TestFightCtx(), a, b);
			long end = System.currentTimeMillis();
			calTime += (end - start);
			if (!r.ok()) {
				role.getGame().notice.message(role, r);
				return stat;
			}
			stat.record(role.getGame(), r.data);
			if ((i + 1) % 1000 == 0) {
				System.out.println("当前战斗了" + (i + 1) + "场 剩余" + (fightNum - i - 1) + "场");
			}
			if ((i + 1) % 2000 == 0) {
				role.sendNow(new FightPlayResp(GDFunc.ADVENTURE, r.data));
			}
		}
		System.out.println("战斗耗时[" + (calTime) + "]ms,单场平均[" + calTime / fightNum + "]ms,单回合平均[" + calTime / stat.getTotalRound() + "]ms");
		System.out.println("平均回合数"+stat.getTotalRound()/stat.getFightNum());
		return stat;
	}


	private static List<Hero> randomHero(GameBody game, List<Hero> listA, List<Hero> listB, int listANum) {
		List<Hero> result = new ArrayList(XT.randomPick(listA, listANum, true));
		Set<Integer> protoIds = result.stream().mapToInt(h -> h.getTpl(game).protoId).boxed().collect(Collectors.toSet());
		int listBNum = 6 - listANum;
		Collections.shuffle(listB);
		for (Hero hero : listB) {
			if (listBNum <= 0) {
				break;
			}
			int protoId = hero.getTpl(game).protoId;
			if (!protoIds.contains(protoId)) {
				protoIds.add(protoId);
				result.add(hero);
				listBNum--;
			}
		}
		return result;
	}

	public static FightTestStat carryFight(String taskName, Role role, int carryStar, int carryNum, int fightNum) {
		FightSide a;
		FightSide b;
		FightTestStat stat = new FightTestStat();
		long calTime = 0L;
		List<Hero> allHero = new ArrayList<>(role.getBag().hero.getList());
		allHero.removeIf(h -> h.lock.heroYoke || h.lock.heroContract);
		List<Hero> carryHero = new ArrayList<>();
		List<Hero> supportHero = new ArrayList<>();
		for (Hero hero : allHero) {
			if (hero.getTpl(role.getGame()).star == carryStar) {
				carryHero.add(hero);
			} else {
				supportHero.add(hero);
			}
		}
		for (int i = 0; i < fightNum; i++) {
			List<Hero> listA = randomHero(role.getGame(), carryHero, supportHero, carryNum);
			List<Hero> listB = randomHero(role.getGame(), carryHero, supportHero, carryNum);
			a = FightTestUtil.buildRandomFightSide(role, listA, GDFight.Team.A);
			b = FightTestUtil.buildRandomFightSide(role, listB, GDFight.Team.B);
			long start = System.currentTimeMillis();
			EcResult<PlayPO> r = role.getGame().play.startBySystem(new TestFightCtx(), a, b);
			long end = System.currentTimeMillis();
			calTime += (end - start);
			if (!r.ok()) {
				return stat;
			}
			stat.record(role.getGame(), r.data);
			if ((i + 1) % 1000 == 0) {
				System.out.println(taskName + "-当前战斗了" + (i + 1) + "场 剩余" + (fightNum - i - 1) + "场");
			}
		}
		System.out.println(taskName + "-战斗耗时[" + (calTime) + "]ms,单场平均[" + calTime / fightNum + "]ms,单回合平均[" + calTime / stat.getTotalRound() + "]ms");
		System.out.println(taskName + "-平均回合数" + stat.getTotalRound() / stat.getFightNum());
		return stat;
	}

	private static FightSide buildRandomFightSide(Role role, List<Hero> list, byte team) {
		list.sort(Comparator.comparing(a -> -a.getTpl(role.getGame()).job));
		FuncPosition funcPosition = new FuncPosition();
		funcPosition.funcId = GDFunc.FIGHT_TEST;
		Position p = new Position();
		p.hallowType = XT.randomIndex(5) + 1;
		p.posNum = 1;
		funcPosition.positions.add(p);
		for (int i = 0; i < list.size(); i++) {
			p.grids.add(new HeroGrid((byte) (i + 1), list.get(i).uid, GDPosition.Origin.BAG));
		}
		role.getFight().addPosition(funcPosition);
		EcResult<FightSide> a = role.getGame().fight.side.createFightSideByRole(team, role, GDFunc.FIGHT_TEST);
		return a.data;
	}

	private static final String BASE_PATH = "../.tmp/";

	public static void out(XSSFWorkbook wb, String name) {
		try {
			String directoryPath = BASE_PATH + TimeTool.getDF1().format(new Date());
			File directoryFile = new File(directoryPath);
			directoryFile.mkdirs();
			String excelPath = directoryFile + File.separator + name + ".xlsx";
			File file = new File(excelPath);
			FileOutputStream fos = new FileOutputStream(file);
			wb.write(fos);
			fos.flush();
			fos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}


	public static void buildSheet(XSSFWorkbook wb, String sheetName, List<TestStat> stats) {
		XSSFSheet sheet = wb.createSheet(sheetName);
		//头
		XSSFRow row1 = sheet.createRow(0);
		int headerIndex = 0;
		for (Field field : TestStat.class.getDeclaredFields()) {
			EExcelNoteField noteField = field.getAnnotation(EExcelNoteField.class);
			if (noteField == null) {
				continue;
			}
			XSSFCell cell = row1.createCell(headerIndex);
			cell.setCellStyle(cellStyle(wb));
			cell.setCellValue(noteField.fieldName());
			sheet.setColumnWidth(headerIndex, noteField.fieldName().getBytes().length * 256 + 512);
			headerIndex++;
		}
		//表中数据
		int roleIndex = 1;
		XSSFCellStyle cellStyle = cellStyle(wb);
		for (TestStat stat : stats) {
			XSSFRow row = sheet.createRow(roleIndex++);
			int cellInt = 0;
			for (Field field : TestStat.class.getDeclaredFields()) {
				EExcelNoteField noteField = field.getAnnotation(EExcelNoteField.class);
				if (noteField == null) {
					continue;
				}
				try {
					XSSFCell cell = row.createCell(cellInt++);
					cell.setCellStyle(cellStyle);
					cell.setCellValue((String) field.get(stat));
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private static XSSFCellStyle cellStyle(XSSFWorkbook wb){
		XSSFCellStyle cellStyle = wb.createCellStyle();
		cellStyle.setAlignment(HorizontalAlignment.CENTER);
		XSSFFont font = wb.createFont();
		font.setFontName("黑体");
		font.setFontHeightInPoints((short) 11);
		cellStyle.setFont(font);
		return cellStyle;
	}
}
